package com.example.review.servicePratice;

import android.os.AsyncTask;
import android.os.Environment;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

//第一个泛型参数指定为String ，表示在执行AsyncTask的时候需要传入一个字符串参数给后台 任务；
// 第二个泛型参数指定为Integer ，表示使用整型数据来作为进度显示单位；
// 第三个泛型 参数指定为Integer ，则表示使用整型数据来反馈执行结果
public class DownloadTask  extends AsyncTask<String, Integer, Integer> {

    //定义了4个整型常量用于表示下载的状态
    public static final int TYPE_SUCCESS = 0;
    public static final int TYPE_FAILED = 1;
    public static final int TYPE_PAUSED = 2;
    public static final int TYPE_CANCELED = 3;


    private DownloadListener listener;
    private boolean isCanceled = false;
    private boolean isPaused = false;
    private int lastProgress;

    public DownloadTask(DownloadListener listener) {
        this.listener=listener;
    }


    /**
     * 这个方法中的所有代码都会在子线程中运行，我们应该在这里去处理所有的耗时任务
     * 这里具体执行下载逻辑
     */
    @Override
    //这里面的参数是调用异步任务传递的参数
    protected Integer doInBackground(String... params) {
        InputStream is = null;
        RandomAccessFile savedFile = null;
        File file = null;

        try{
            long downloadedLength = 0; // 记录已下载的文件长度
            String downloadUrl = params[0];
            String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
            //指定到sd卡的download目录下面
            String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
            file = new File(directory + fileName);
            if (file.exists()){
                //刚开始file为空，所以下载进度也为0
                downloadedLength=file.length();
            }
            long contentLength=getContentLength(downloadUrl);//获取下载文件的总长度,将其赋值给一个变量contentLength。
            if (contentLength == 0) {
                return TYPE_FAILED;
            } else if (contentLength == downloadedLength) {
                // 已下载字节和文件总字节相等，说明已经下载完成了
                return TYPE_SUCCESS;
            }
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
            // 断点下载，指定从哪个字节开始下载
            .addHeader("RANGE", "bytes=" + downloadedLength + "-")
                    .url(downloadUrl)
                    .build();
            Response response = client.newCall(request).execute();
            if(response != null){
                is = response.body().byteStream();
                savedFile = new RandomAccessFile(file, "rw");
                savedFile.seek(downloadedLength); // 跳过已下载的字节
                byte[] b = new byte[1024];
                int total = 0;
                int len;
                while ((len = is.read(b)) != -1) {
                    if (isCanceled) {
                        return TYPE_CANCELED;
                    } else if (isPaused) {
                        return TYPE_PAUSED;
                    } else {
                        total += len;
                        savedFile.write(b, 0, len); // 计算已下载的百分比
                        int progress = (int) ((total + downloadedLength) * 100 / contentLength);
                        //调用publishProgress() 方法进行通 知
                        publishProgress(progress);
                    }
                }
                response.body().close(); return TYPE_SUCCESS; }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (savedFile != null) {
                    savedFile.close();
                }
                if (isCanceled && file != null) {
                    file.delete();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return TYPE_FAILED;
    }

    public DownloadTask() {
        super();
    }


    /**
     * 当在doInBackground（）中调用了publishProgress(Progress...) 方法 后，
     * onProgressUpdate (Progress...) 方法就会很快被调用
     * 更新下载进度
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        int progress = values[0];
        //如果当前进度大于原来进度，通知下载更新，并将当前进度赋值给上一次进度
        if (progress > lastProgress) {
            listener.onProgress(progress);
            lastProgress = progress;
        }
    }


    /**
     * 当后台任务执行完毕并通过return 语句进行返回时，这个方法就很快会被调用
     * 可以利用返回的数据来进行一些UI操作，比如说提醒任 务执行的结果，以及关闭掉进度条对话框等
     * 用于通知最终的下载结果 就是根据参数中传入的下载状态来进行回调
     */
    @Override
    protected void onPostExecute(Integer status) {
        super.onPostExecute(status);
        switch (status){
            case TYPE_SUCCESS:
                listener.onSuccess();
                break;
            case TYPE_FAILED:
                listener.onFailed();
                break;
            case TYPE_PAUSED:
                listener.onPaused();
                break;
            case TYPE_CANCELED:
                listener.onCanceled();
                break;
            default:
                break;
        }
    }

    public void pauseDownload() {
        isPaused = true;
    }



    public void cancelDownload() {
        isCanceled = true;
    }


    /**
     * 获取待下载文件大小
     * 思路就是发送一个http请求，通过计算响应数据的大小得知待下载文件大小
     */
    private long getContentLength(String downloadUrl) throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(downloadUrl)
                .build();
        Response response = client.newCall(request).execute();
        if (response != null && response.isSuccessful()) {
            long contentLength = response.body().contentLength();
            response.body().close();
            return contentLength;
        }
        return 0;
    }

}
